package com.blackhorse.demo.myBeanFactory.factory;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * 使用工厂模式解耦
 * BeanFactory：创建一个工厂用于生成Bean对象
 * Bean：表示可重用组件
 * JavaBean：用JAVA语言写的可重用组件
 * JavaBean的范围大于实体类
 * <p>
 * 解耦方法：
 * 1）通过配置文件（properties或xml）配置service和dao
 * 配置内容：唯一标识和全限定类名
 * 2）读取配置文件，通过反射机制创建对象
 * <p>
 * 为了方便这里采用properties配置文件
 */
public class MyBeanFactory {

    //定义容器存放创建的单例对象
    private static Map<String, Object> beans;

    //配置文件
    private static Properties properties;

    //加载配置文件
    static {
        try {
            properties = new Properties();
            //使用类加载器获取配置文件路径
            InputStream is = MyBeanFactory.class.getClassLoader()
                    .getResourceAsStream("mybean.properties");
            properties.load(is);

            //实例化容器
            beans = new HashMap<String, Object>();
            //读取配置文件的key，要保证先读到dao，因为service依赖于dao
            Set<String> keys = properties.stringPropertyNames();
            LinkedList<String> list = new LinkedList<>();//按照先dao再service的顺序保存key
            for (String key : keys) {
                if (key.contains("dao") || key.contains("Dao")) {
                    list.addFirst(key);
                } else {
                    list.addLast(key);
                }
            }
            for (String key : list) {
               // System.out.println("key:" + key);
                String beanPath = properties.getProperty(key);
                //反射创建对象
                Object bean = Class.forName(beanPath).newInstance();
                //存入容器
                beans.put(key, bean);
            }
        } catch (IOException e) {
            //加载失败，直接抛出Error，程序不再向下执行
            throw new ExceptionInInitializerError("加载properties配置文件失败！");
        } catch (IllegalAccessException | InstantiationException e) {
            throw new ExceptionInInitializerError("对象实例化失败！");
        } catch (ClassNotFoundException e) {
            throw new ExceptionInInitializerError("找不到要实例化的类！");
        }
    }

    /**
     * 根据bean名称获取bean对象
     * 存在的问题：每次获取bean都会创建新对象（多例对象）
     *
     * @param beanName
     * @return
     */
    /*
    public static <T> T getBean(String beanName) {
        T bean = null;
        try {
            String beanPath = properties.getProperty(beanName);
            bean = (T) Class.forName(beanPath).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }

    public static <T> T getBean(String beanName, Class<T> beanClass) {
        T bean = null;
        try {
            String beanPath = properties.getProperty(beanName);
            bean = (T) Class.forName(beanPath).newInstance();//每次获取bean都会创建新对象（多例对象）
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }
    */

    /**
     * 根据bean名称获取bean对象
     * 从容器中获得的对象即是单例对象
     *
     * @param beanName
     * @return
     */
    public static <T> T getBean(String beanName) {
        return (T) beans.get(beanName);
    }

    public static <T> T getBean(String beanName, Class<T> beanClass) {
        return (T) beans.get(beanName);
    }
}
